package metamodel.transformer;

import metamodel.ClassNode;
import metamodel.Property;
import metamodel.transformer.BottomUpGraphTransformer;

import java.util.*;

/**
 * User: fleipold
 * Date: 06-Dec-2007
 * Time: 22:52:10
 */
public class BottomUpTransformingGraphTraverser extends BottomUpTransformingTreeTraverser {
    private final BottomUpGraphTransformer visitor;
    private final Map<Object, Object> map = new HashMap<Object, Object>();
    private final Set<Object> visited = new HashSet<Object>();
    
    private final List<Cycle> cycles= new ArrayList<Cycle>();

    public BottomUpTransformingGraphTraverser(ClassNode classNode, BottomUpGraphTransformer visitor) {
        super(classNode, visitor);

        this.visitor = visitor;
    }

    protected void visit(Object object) {
        visited.add(object);
    }

    protected void mappedObject(Object originalObject, Object mappedObject) {
        map.put(originalObject,mappedObject);
    }

    protected boolean checkCycling(Property meta, Object parentObject, Object propValue) {
        if (visited.contains(propValue)){
            cycles.add(new Cycle(meta, parentObject, propValue));
            return true;
        }
        return false;
    }

    protected void resolveCycles() {
        for (Cycle cycle : cycles) {
            visitor.mapCycle(cycle.getMeta(),
                    map.get(cycle.getSource()), cycle.getSource(),
                    map.get(cycle.getDestination()), cycle.getDestination()
            );
        }
    }

    private class Cycle {
        private final Object   source, destination;
        private final Property meta;

        public Cycle(Property meta, Object source, Object destination) {
            this.meta = meta;
            this.destination = destination;
            this.source = source;
        }

        public Object getSource() {
            return source;
        }

        public Object getDestination() {
            return destination;
        }

        public Property getMeta() {
            return meta;
        }
    }
}
